// can_loopback_test.c

/****************************************************************************
 *
 * 功能: CAN总线双设备回环测试程序
 *
 * 描述:
 *  本程序用于测试两个CAN设备之间的通信。它会同时初始化两个指定的CAN接口
 *  (例如 vcan0 和 vcan1)，然后通过多线程模拟它们互相收发数据。
 *  - 设备1 向 设备2 发送数据 (ID: can_id)
 *  - 设备2 向 设备1 发送数据 (ID: can_id + 1)
 *  每个设备都会启动一个接收线程和一个发送线程。接收线程会验证收到的
 *  CAN帧ID和数据内容是否正确。发送线程会连续发送100帧数据。
 *  程序支持通过命令行参数动态配置CAN接口、CAN ID、比特率和采样点。
 *
 * 编译方法:
 *  gcc -o can_loopback_test can_loopback_test.c -lpthread
 *
 * 使用方法:
 *  sudo ./can_loopback_test <ifname1> <ifname2> <can_id_base> <bitrate> <sample_point>
 *
 * 示例 (使用虚拟CAN):
 *  # 1. 加载vcan内核模块
 *  sudo modprobe vcan
 *  # 2. 创建两个虚拟CAN接口
 *  sudo ip link add dev vcan0 type vcan
 *  sudo ip link add dev vcan1 type vcan
 *  # 3. 运行测试程序
 *  sudo ./can_loopback_test vcan0 vcan1 500 500000 0.875
 *  (vcan0 <-> vcan1, 基础ID=500, 比特率=500kbps, 采样点=87.5%)
 *
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#define NUM_FRAMES 100 // 每次测试发送的帧数

// 用于向线程传递参数的结构体
typedef struct {
    int thread_id;              // 线程标识 (0 或 1)
    char ifname[IFNAMSIZ];        // CAN接口名, e.g., "can0"
    int can_sock;               // CAN套接字
    canid_t send_id;            // 发送时使用的CAN ID
    canid_t recv_id;            // 期望接收的CAN ID
    int bitrate;                // 比特率
    double sample_point;        // 采样点
    
    // 统计信息
    int send_count;
    int recv_count;
    int error_count;
} can_dev_t;

/**
 * @brief 打印CAN帧内容
 * @param frame 指向can_frame结构体的指针
 * @param prefix 打印前缀字符串
 */
void print_can_frame(const struct can_frame *frame, const char *prefix) {
    printf("%s %03X#", prefix, frame->can_id);
    for (int i = 0; i < frame->can_dlc; i++) {
        printf(" %02X", frame->data[i]);
    }
    printf("\n");
}

/**
 * @brief 配置并启动CAN设备
 * @param ifname CAN接口名
 * @param bitrate 比特率
 * @param sample_point 采样点
 * @return 0 表示成功, -1 表示失败
 */
int setup_can_device(const char *ifname, int bitrate, double sample_point) {
    char cmd[256];

    printf("正在配置 %s...\n", ifname);

    // 1. 关闭设备，以便进行配置
    sprintf(cmd, "ip link set %s down", ifname);
    if (system(cmd) != 0) {
        fprintf(stderr, "错误：无法关闭 %s\n", ifname);
        return -1;
    }
    memset(cmd, 0, sizeof(cmd));

    // 2. 设置CAN模式、比特率和采样点
    //    内核会根据比特率和采样点自动计算TQ等参数

    // 2. 设置CAN模式、比特率和采样点
    //    内核会根据比特率和采样点自动计算TQ等参数
    // sprintf(cmd, "ip link set %s type can bitrate %d sample-point %.3f", ifname, bitrate, sample_point);
    sprintf(cmd, "ip link set %s type can tq 200 prop-seg 5 phase-seg1 7 phase-seg2 7 sjw 4", ifname);
    if (system(cmd) != 0) {
        fprintf(stderr, "错误：无法为 %s 设置比特率/采样点\n", ifname);
        return -1;
    }
    memset(cmd, 0, sizeof(cmd));

    // 3. 启动设备
    sprintf(cmd, "ip link set %s up", ifname);
    if (system(cmd) != 0) {
        fprintf(stderr, "错误：无法启动 %s\n", ifname);
        return -1;
    }
    memset(cmd, 0, sizeof(cmd));
    
    sprintf(cmd, "ip -d link show %s", ifname);
    if (system(cmd) != 0) {
        fprintf(stderr, "错误：link show %s failed\n", ifname);
        return -1;
    }
    memset(cmd, 0, sizeof(cmd));
    return 0;
}

/**
 * @brief 关闭CAN设备
 * @param ifname CAN接口名
 */
void teardown_can_device(const char *ifname) {
    char cmd[256];
    printf("Bringing down %s...\n", ifname);
    sprintf(cmd, "ip link set %s down", ifname);
    system(cmd);
}

/**
 * @brief 接收线程函数
 * @param arg 指向 can_dev_t 结构体的指针
 * @return NULL
 */
void *receiver_thread(void *arg) {
    can_dev_t *dev = (can_dev_t *)arg;
    struct can_frame frame;
    int expected_seq = 0;

    printf("[Receiver %d on %s]: Thread started. Waiting for %d frames with ID 0x%X.\n",
           dev->thread_id, dev->ifname, NUM_FRAMES, dev->recv_id);

    while (dev->recv_count < NUM_FRAMES) {
        ssize_t nbytes = read(dev->can_sock, &frame, sizeof(struct can_frame));

        if (nbytes < 0) {
            // read() 超时是正常情况，因为发送方可能还没开始发送
            // 或者发送有延迟。我们不将其计为硬错误，但如果一直超时，
            // 循环将不会结束，主线程的join会等待。
            perror("read error");
            // 在实际应用中可以增加一个超时计数器来决定是否提前退出
            continue;
        }

        if (nbytes < sizeof(struct can_frame)) {
            fprintf(stderr, "[Receiver %d on %s]: Incomplete CAN frame received.\n", dev->thread_id, dev->ifname);
            dev->error_count++;
            continue;
        }

        // --- 数据校验 ---
        // 1. 校验CAN ID
        if (frame.can_id != dev->recv_id) {
            fprintf(stderr, "[Receiver %d on %s]: Error! Expected ID 0x%X, but got 0x%X\n",
                    dev->thread_id, dev->ifname, dev->recv_id, frame.can_id);
            dev->error_count++;
            // 即使ID错误，也认为消耗了一个帧的接收机会
            dev->recv_count++; 
            continue;
        }

        // 2. 校验数据长度
        if (frame.can_dlc != 8) {
             fprintf(stderr, "[Receiver %d on %s]: Error! Expected DLC 8, but got %d\n",
                    dev->thread_id, dev->ifname, frame.can_dlc);
            dev->error_count++;
            dev->recv_count++;
            continue;
        }
        
        // 3. 校验数据内容 (序列号)
        unsigned char seq = frame.data[0];
        if (seq != expected_seq) {
             fprintf(stderr, "[Receiver %d on %s]: Error! Data mismatch. Expected seq %x, but got %x\n",
                    dev->thread_id, dev->ifname, expected_seq, seq);
            dev->error_count++;
        }
        
        // 无论校验是否成功，都打印收到的帧（用于调试）
        print_can_frame(&frame, "[Receiver] RX:");

        dev->recv_count++;
        expected_seq++;
    }

    printf("[Receiver %d on %s]: Thread finished. Total processed: %d, Errors: %d.\n",
           dev->thread_id, dev->ifname, dev->recv_count, dev->error_count);
    return NULL;
}

/**
 * @brief 发送线程函数
 * @param arg 指向 can_dev_t 结构体的指针
 * @return NULL
 */
void *sender_thread(void *arg) {
    can_dev_t *dev = (can_dev_t *)arg;
    struct can_frame frame;

    printf("[Sender %d on %s]: Thread started. Sending %d frames with ID 0x%X.\n",
           dev->thread_id, dev->ifname, NUM_FRAMES, dev->send_id);
    
    // 稍微延时，确保接收线程已完全准备好
    usleep(100 * 1000);

    memset(&frame, 0, sizeof(frame));
    frame.can_id = dev->send_id;
    frame.can_dlc = 8;

    for (int i = 0; i < NUM_FRAMES; i++) {
        frame.data[0] = i; // 使用第一个字节作为序列号
        frame.data[1] = 0xDE;
        frame.data[2] = 0xAD;
        frame.data[3] = 0xBE;
        frame.data[4] = 0xEF;
        frame.data[5] = dev->thread_id; // 在数据中标识发送源
        frame.data[6] = 0xCA;
        frame.data[7] = 0xFE;

        ssize_t nbytes = write(dev->can_sock, &frame, sizeof(struct can_frame));
        if (nbytes != sizeof(struct can_frame)) {
            perror("write error");
            fprintf(stderr, "[Sender %d on %s]: Failed to send frame %d\n", dev->thread_id, dev->ifname, i);
        } else {
            dev->send_count++;
        }
        
        // 添加微小延时，模拟真实世界应用并避免总线负载过高
        usleep(100 * 1000); // 100ms delay
    }

    printf("[Sender %d on %s]: Thread finished. Total sent: %d.\n",
           dev->thread_id, dev->ifname, dev->send_count);
    return NULL;
}


int main(int argc, char **argv) {
    if (argc != 6) {
        fprintf(stderr, "Usage: %s <ifname1> <ifname2> <can_id_base> <bitrate> <sample_point>\n", argv[0]);
        fprintf(stderr, "Example: sudo %s vcan0 vcan1 500 500000 0.875\n", argv[0]);
        return 1;
    }

    char *ifname1 = argv[1];
    char *ifname2 = argv[2];
    canid_t can_id_base = (canid_t)atoi(argv[3]);
    int bitrate = atoi(argv[4]);
    double sample_point = atof(argv[5]);

    int loop_count = 1;
    while(1) {
        printf("\n<<<<<<<<<<<<<<<<< Starting Test Loop %d >>>>>>>>>>>>>>>>>\n", loop_count++);

        // --- 1. 初始化并启动CAN设备 ---
        if (setup_can_device(ifname1, bitrate, sample_point) != 0) return 1;
        if (setup_can_device(ifname2, bitrate, sample_point) != 0) return 1;

        // --- 2. 创建并配置套接字 ---
        can_dev_t dev1 = {0}, dev2 = {0};

        // 配置设备1
        dev1.thread_id = 1;
        strcpy(dev1.ifname, ifname1);
        dev1.send_id = can_id_base;
        dev1.recv_id = can_id_base + 1;

        // 配置设备2
        dev2.thread_id = 2;
        strcpy(dev2.ifname, ifname2);
        dev2.send_id = can_id_base + 1;
        dev2.recv_id = can_id_base;

        // 打开套接字
        if ((dev1.can_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
            perror("socket dev1"); return 1;
        }
        if ((dev2.can_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
            perror("socket dev2"); return 1;
        }

        // 绑定到接口
        struct ifreq ifr;
        strcpy(ifr.ifr_name, dev1.ifname);
        ioctl(dev1.can_sock, SIOCGIFINDEX, &ifr);
        struct sockaddr_can addr1 = {.can_family = AF_CAN, .can_ifindex = ifr.ifr_ifindex};
        
        strcpy(ifr.ifr_name, dev2.ifname);
        ioctl(dev2.can_sock, SIOCGIFINDEX, &ifr);
        struct sockaddr_can addr2 = {.can_family = AF_CAN, .can_ifindex = ifr.ifr_ifindex};

        if (bind(dev1.can_sock, (struct sockaddr *)&addr1, sizeof(addr1)) < 0) {
            perror("bind dev1"); return 1;
        }
        if (bind(dev2.can_sock, (struct sockaddr *)&addr2, sizeof(addr2)) < 0) {
            perror("bind dev2"); return 1;
        }

        // --- 3. 设置接收过滤器和超时 ---
        // 这是非常重要的一步，可以确保只接收我们感兴趣的CAN ID，减轻CPU负担
        struct can_filter rfilter1 = {.can_id = dev1.recv_id, .can_mask = CAN_SFF_MASK};
        setsockopt(dev1.can_sock, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter1, sizeof(rfilter1));
        
        struct can_filter rfilter2 = {.can_id = dev2.recv_id, .can_mask = CAN_SFF_MASK};
        setsockopt(dev2.can_sock, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter2, sizeof(rfilter2));

        // 设置接收超时 (1秒)
        struct timeval tv = {.tv_sec = 5, .tv_usec = 0};
        setsockopt(dev1.can_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
        setsockopt(dev2.can_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

        // --- 4. 创建并启动线程 ---
        pthread_t recv_tid1, recv_tid2, send_tid1, send_tid2;

        printf("Starting threads...\n");
        // 首先启动接收线程
        pthread_create(&recv_tid1, NULL, receiver_thread, &dev1);
        pthread_create(&recv_tid2, NULL, receiver_thread, &dev2);

        // 然后启动发送线程
        pthread_create(&send_tid1, NULL, sender_thread, &dev1);
        pthread_create(&send_tid2, NULL, sender_thread, &dev2);

        // --- 5. 等待所有线程结束 ---
        pthread_join(send_tid1, NULL);
        pthread_join(send_tid2, NULL);
        pthread_join(recv_tid1, NULL);
        pthread_join(recv_tid2, NULL);
        printf("All threads have finished.\n");

        // --- 6. 关闭套接字 ---
        close(dev1.can_sock);
        close(dev2.can_sock);

        // --- 7. 关闭CAN设备 ---
        teardown_can_device(ifname1);
        teardown_can_device(ifname2);

        // --- 8. 报告最终结果 ---
        printf("\n=============== TEST SUMMARY ===============\n");
        printf("Device 1 (%s):\n", dev1.ifname);
        printf("  - Sent: %d/%d frames\n", dev1.send_count, NUM_FRAMES);
        printf("  - Received: %d/%d frames\n", dev1.recv_count, NUM_FRAMES);
        printf("  - Verification Errors: %d\n", dev1.error_count);
        
        printf("Device 2 (%s):\n", dev2.ifname);
        printf("  - Sent: %d/%d frames\n", dev2.send_count, NUM_FRAMES);
        printf("  - Received: %d/%d frames\n", dev2.recv_count, NUM_FRAMES);
        printf("  - Verification Errors: %d\n", dev2.error_count);

        if (dev1.error_count == 0 && dev2.error_count == 0 &&
            dev1.recv_count == NUM_FRAMES && dev2.recv_count == NUM_FRAMES) {
            printf("\nRESULT: SUCCESS! Loopback test passed.\n");
        } else {
            printf("\nRESULT: FAILED! Check error messages above.\n");
            // --- 9. 出错后 询问是否继续 ---
            printf("\nPress Enter to run the next test loop, or Ctrl+C to exit...(loop_count = %d, NUM_FRAMES = %d)\n", loop_count, NUM_FRAMES);
            break;
            
            // char c = '\0';
            // while (c != 'c') {
            //     c = getchar();
            //     printf("%c <<<---\n", c);
            //     sleep(1);
            // }
            // getchar();
            // getchar();
            // getchar();
        }
    }
    printf("==========================================\n");

    

    
    return 0;
}
